﻿using System;
using System.Collections.Generic;
using Fleck;
using Newtonsoft.Json;

public class Program
{
    static uint UserIndex = 0;
    static Dictionary<uint, User> userDict = new Dictionary<uint, User>();
    static Dictionary<IWebSocketConnection, uint> socketDict = new Dictionary<IWebSocketConnection, uint>();
    static Dictionary<uint, Room> roomDict = new Dictionary<uint, Room>();

    class User
    {
        public uint Id;
        public uint room;
        public IWebSocketConnection socket;
    }

    class Room
    {
        public string type;
        public uint maxUserCount;
        public List<uint> userList = new List<uint>();
        public bool IsFull()
        {
            return userList.Count >= maxUserCount;
        }
    }

    public static void Main()
    {
        WebSocketServer server1 = new WebSocketServer("ws://0.0.0.0:8181");
        WebSocketServer server2 = new WebSocketServer("ws://[::]:8181");
        server1.Start(SocketConfig);
        server2.Start(SocketConfig);
        while (true)
        {
            Console.ReadKey();
        }
    }

    public static void SocketConfig(IWebSocketConnection socket)
    {
        socket.OnOpen = () =>
        {
            uint userId = ++UserIndex;
            //Console.WriteLine(userId + " Connected");
            User user = new User();
            user.Id = userId;
            user.socket = socket;
            userDict[userId] = user;
            socketDict[socket] = userId;

            JavaScriptObject jo = new JavaScriptObject();
            jo["_"] = "user";
            jo["user"] = userId;
            socket.Send(JavaScriptConvert.SerializeObject(jo));
        };
        socket.OnClose = () =>
        {
            uint userId = socketDict[socket];
            //Console.WriteLine(userId + " Disconnected");
            QuitRoom(userId);
            userDict.Remove(userId);
            socketDict.Remove(socket);
        };
        socket.OnMessage = message =>
        {
            //Console.WriteLine(message);
            try
            {
                uint userId = socketDict[socket];
                JavaScriptObject jo = JavaScriptConvert.DeserializeObject<JavaScriptObject>(message);
                switch (jo["_"].ToString())
                {
                    case "createRoom":
                        {
                            QuitRoom(userId);
                            uint count = uint.Parse(jo["count"].ToString());
                            Room room = new Room();
                            room.type = jo["type"].ToString();
                            room.maxUserCount = count;
                            room.userList.Add(userId);
                            roomDict[userId] = room;
                            userDict[userId].room = userId;
                            socket.Send(message);
                        }
                        break;
                    case "getRoom":
                        {
                            CheckSocketAvailable();
                            string type = jo["type"].ToString();
                            JavaScriptArray list = new JavaScriptArray();
                            foreach (var room in roomDict)
                            {
                                if (room.Value.type == type && !room.Value.IsFull())
                                    list.Add(room.Key);
                            }
                            jo["list"] = list;
                            socket.Send(JavaScriptConvert.SerializeObject(jo));
                        }
                        break;
                    case "joinRoom":
                        {
                            uint room = uint.Parse(jo["room"].ToString());
                            if (!roomDict.ContainsKey(room))
                            {
                                socket.Send("{\"error\":\"房间已关闭\"}");
                                break;
                            }
                            if (roomDict[room].IsFull())
                            {
                                socket.Send("{\"error\":\"房间玩家已满\"}");
                                break;
                            }
                            if (room == userId)
                            {
                                socket.Send("{\"error\":\"你已经在此房间了\"}");
                                break;
                            }
                            QuitRoom(userId);
                            roomDict[room].userList.Add(userId);
                            userDict[userId].room = room;
                            jo["user"] = userId;
                            jo["start"] = roomDict[room].IsFull() ? 1 : 0;
                            string msg = JavaScriptConvert.SerializeObject(jo);
                            roomDict[room].userList.ForEach(p => {
                                userDict[p].socket.Send(msg);
                            });
                        }
                        break;
                    case "quitRoom":
                        {
                            QuitRoom(userId);
                        }
                        break;
                    default:
                        {
                            uint roomId = userDict[userId].room;
                            if (!roomDict.ContainsKey(roomId))
                                break;
                            Room room = roomDict[roomId];
                            room.userList.ForEach(p => {
                                userDict[p].socket.Send(message);
                            });
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        };
    }

    static void QuitRoom(uint userId)
    {
        uint roomId = userDict[userId].room;
        if (!roomDict.ContainsKey(roomId))
            return;
        Room room = roomDict[roomId];
        if (roomId == userId)
            roomDict.Remove(roomId);
        room.userList.Remove(userId);
        userDict[userId].room = 0;
        JavaScriptObject jo = new JavaScriptObject();
        jo["_"] = "quitRoom";
        jo["user"] = userId;
        jo["disband"] = roomId == userId ? 1 : 0;
        string msg = JavaScriptConvert.SerializeObject(jo);
        room.userList.ForEach(p => {
            if(roomId == userId)
                userDict[p].room = 0;
            userDict[p].socket.Send(msg);
        });
    }

    static void CheckSocketAvailable()
    {
        List<IWebSocketConnection> notAvailableList = null;
        foreach (var kp in socketDict)
        {
            if (!kp.Key.IsAvailable)
            {
                if (notAvailableList == null)
                    notAvailableList = new List<IWebSocketConnection>();
                notAvailableList.Add(kp.Key);
            }
        }
        if (notAvailableList != null)
        {
            foreach (var socket in notAvailableList)
            {
                socket.OnClose?.Invoke();
            }
        }
    }
}

